"""
播放队列功能模块，包含话术队列相关的UI和功能
"""
import os
import uuid
import random
import threading
import traceback
from PyQt5.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QTableWidget, 
    QTableWidgetItem, QHeaderView, QPushButton, QLabel, QComboBox,
    QMessageBox, QMenu, QAction, QAbstractItemView
)
from PyQt5.QtCore import Qt, pyqtSignal, QSize, QTimer, QMetaObject, Q_ARG, QVariant, pyqtSlot
from PyQt5.QtGui import QColor, QBrush, QIcon, QCursor, QFont

from app.ui.components.styled_button import StyledButton
from app.ui.components.styled_label import StyledLabel
from app.utils.logger import Logger
from app.services.service_locator import ServiceLocator
from app.services.audio_service import IAudioService
from app.services.speech_service import ISpeechService
from app.models.audio_item import AudioItem
from app.utils.speech_text_processor import SpeechTextProcessor
from app.config.app_config import AppConfig
from app.services.config_service import IConfigService
from app.state.app_state_manager import AppStateManager
from app.state.state_types import StateType, StateValues
from app.services.ai_service import IAIService
from app.services.websocket_service import IWebSocketService
from app.services.script_service import IScriptService
from app.services.comment_service import ICommentService


class PlaylistModule(QWidget):
    """
    播放队列功能模块类，包含话术队列相关的UI和功能
    """
    
    # 定义信号
    queue_updated = pyqtSignal(list)  # 队列更新信号
    playback_started = pyqtSignal(object)  # 播放开始信号
    playback_completed = pyqtSignal(object)  # 播放完成信号
    
    # 信号定义
    playback_started_signal = pyqtSignal()
    playback_stopped_signal = pyqtSignal()
    playback_paused_signal = pyqtSignal()
    playback_resumed_signal = pyqtSignal()
    queue_updated_signal = pyqtSignal()
    current_item_changed_signal = pyqtSignal(object)  # AudioItem
    generation_progress_signal = pyqtSignal(int, int)  # current, total
    
    def __init__(self, parent=None):
        """
        初始化播放队列功能模块
        """
        super().__init__(parent)
        
        # 设置对象名称，便于查找
        self.setObjectName("playlist_module")
        
        # 获取日志记录器
        self.logger = Logger.get_logger("playlist_module")
        
        # 初始化成员变量
        self.playlist_table = None
        self.audio_service = None
        self.speech_service = None
        
        # 添加生成停止标志
        self._generation_stopped = False
        
        # 创建定时刷新计时器
        self._refresh_timer = QTimer(self)
        self._refresh_timer.timeout.connect(self._refresh_current_playback)
        
        # 获取服务
        self._setup_services()
        
        # 初始化UI
        self._init_ui()
        
        # 连接信号
        self._connect_signals()
        
        # 启动定时刷新
        self._refresh_timer.start(1000)  # 每秒刷新一次
        
        # 初始化队列 - 使用单次触发的方式，避免QBasicTimer错误
        self.init_timer = QTimer(self)
        self.init_timer.setSingleShot(True)
        self.init_timer.timeout.connect(self._init_queue)
        self.init_timer.start(1000)
        
        self.logger.info("播放队列模块初始化完成")
    
    def _setup_services(self):
        """
        设置服务
        """
        # 获取音频服务
        self.audio_service = ServiceLocator.get(IAudioService)
        if not self.audio_service:
            self.logger.warning("无法获取音频服务")
        
        # 获取语音服务
        self.speech_service = ServiceLocator.get(ISpeechService)
        if not self.speech_service:
            self.logger.warning("无法获取语音服务")
        
        # 绑定服务
        self.ai_service = ServiceLocator.get(IAIService)
        self.websocket_service = ServiceLocator.get(IWebSocketService)
        self.config_service = ServiceLocator.get(IConfigService)
        self.script_service = ServiceLocator.get(IScriptService)
        self.comment_service = ServiceLocator.get(ICommentService)
        
        # 获取状态管理器
        self.state_manager = ServiceLocator.get(AppStateManager)
        if not self.state_manager:
            self.logger.error("无法获取状态管理器，状态更新可能不正确")
    
    def _init_ui(self):
        """
        初始化UI
        """
        # 创建主布局
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(5)
        
        # 创建播放队列组
        self._create_playlist_group()
        
        # 设置面板宽度
        self.setMinimumWidth(250)
        self.setMaximumWidth(350)
        
        # 启动定时器，用于刷新播放状态
        self.refresh_timer = QTimer(self)
        self.refresh_timer.timeout.connect(self._refresh_playback_status)
        self.refresh_timer.start(1000)  # 每秒刷新一次
    
    def _create_playlist_group(self):
        """
        创建播放队列组
        """
        # 播放队列组
        playlist_group = QGroupBox("播放队列")
        playlist_inner_layout = QVBoxLayout(playlist_group)
        
        # 创建顶部按钮区域
        top_buttons_layout = QHBoxLayout()
        
        # 创建刷新按钮
        self.refresh_button = QPushButton("刷新全部话术")
        self.refresh_button.setToolTip("重新加载所有主话术文件到队列中")
        self.refresh_button.clicked.connect(self.reload_all_speeches)
        self.refresh_button.setStyleSheet("""
            QPushButton {
                background-color: #4CAF50;
                color: white;
                border: none;
                padding: 5px 10px;
                border-radius: 3px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #45a049;
            }
            QPushButton:pressed {
                background-color: #3d8b40;
            }
        """)
        top_buttons_layout.addWidget(self.refresh_button)
        
        # 添加弹性空间
        top_buttons_layout.addStretch(1)
        
        # 添加顶部按钮区域到布局
        playlist_inner_layout.addLayout(top_buttons_layout)
        
        # 创建播放队列表格
        self.playlist_table = QTableWidget(0, 2)
        self.playlist_table.setHorizontalHeaderLabels(["类型", "话术"])
        self.playlist_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.playlist_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.playlist_table.verticalHeader().setVisible(False)
        # 设置表格为只读模式
        self.playlist_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # 设置表格选择模式为单行选择
        self.playlist_table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.playlist_table.setSelectionMode(QAbstractItemView.SingleSelection)
        
        # 设置表格样式
        self.playlist_table.setStyleSheet("""
            QTableWidget {
                border: 1px solid #ddd;
                border-radius: 3px;
                background-color: #fff;
            }
            QTableWidget::item {
                padding: 5px;
            }
            QTableWidget::item:selected {
                background-color: #e6f7ff;
                color: #000;
            }
            QHeaderView::section {
                background-color: #f5f5f5;
                padding: 5px;
                border: 1px solid #ddd;
                font-weight: bold;
            }
        """)
        
        playlist_inner_layout.addWidget(self.playlist_table)
        
        self.layout.addWidget(playlist_group)
    
    def _connect_signals(self):
        """
        连接信号
        """
        # 如果音频服务可用，连接其信号
        if self.audio_service:
            self.audio_service.add_listener("audio_queue_updated", self._on_audio_queue_updated)
            self.audio_service.add_listener("audio_playback_started", self._on_audio_playback_started)
            self.audio_service.add_listener("audio_playback_completed", self._on_audio_playback_completed)
            self.audio_service.add_listener("audio_playback_stopped", self._on_audio_playback_stopped)
            self.audio_service.add_listener("audio_playback_paused", self._on_audio_playback_paused)
            self.audio_service.add_listener("audio_playback_resumed", self._on_audio_playback_resumed)
            self.audio_service.add_listener("audio_playback_failed", self._on_audio_playback_failed)
    
    def _process_queue_text(self, queue):
        """
        处理队列中的文本内容，执行变量替换和随机文本选择
        
        @param queue: 音频队列
        """
        for item in queue:
            # 只处理未处理过的文本
            if not item.metadata.get("processed", False) and item.title:
                # 准备变量字典
                variables = {
                    "item_type": item.metadata.get("original_type", "未知"),
                    "file_name": item.metadata.get("file_name", "未知文件"),
                    "is_temporary": item.metadata.get("is_temporary", False)
                }
                
                # 记录原始文本
                original_text = item.metadata.get("original_text", item.title)
                
                # 检查是否有随机文本格式需要处理
                # has_random_text = "{" in original_text and "|" in original_text and "}" in original_text
                
                # if has_random_text:
                #     self.logger.info(f"[文本处理] 队列项检测到随机文本格式: {original_text[:50]}...")
                
                # 使用SpeechTextProcessor完整处理话术内容
                processed_text = SpeechTextProcessor.process_speech(original_text, variables)
                
                # 更新文本和元数据
                item.title = processed_text
                item.metadata["processed"] = True
                item.metadata["original_text"] = original_text
                
                # self.logger.info(f"[文本处理] 队列项处理前: {original_text[:50]}...")
                # self.logger.info(f"[文本处理] 队列项处理后: {processed_text[:50]}...")
                self.logger.info(f"[话术] : {processed_text[:50]}...")
    
    def _on_audio_queue_updated(self, event):
        """
        音频队列更新事件处理
        
        @param event: 事件对象
        """
        try:
            # 获取最新队列
            queue = event.data
            
            # 处理队列中未处理的文本
            self._process_queue_text(queue)
            
            # 获取当前播放项
            current_item = None
            if self.audio_service:
                current_item = self.audio_service.get_current_item()
            
            # 计算实际显示项目数（队列长度+当前播放项）
            display_count = len(queue) + (1 if current_item else 0)
            
            # 创建一个副本，避免线程间的数据竞争
            queue_copy = queue.copy() if queue else []
            
            # 使用QTimer.singleShot在主线程中更新UI
            QTimer.singleShot(0, lambda: self._update_playlist_table(queue_copy))
            
            # 发送队列更新信号
            self.queue_updated.emit(queue_copy)
        except Exception as e:
            self.logger.error(f"处理音频队列更新事件异常: {str(e)}")
            traceback.print_exc()
    
    def _on_audio_playback_started(self, event):
        """
        处理音频播放开始事件
        
        @param event: 事件对象
        """
        # 获取音频项
        audio_item = event.data
        
        # 使用QTimer.singleShot在主线程中发送信号
        QTimer.singleShot(0, lambda: self.playback_started.emit(audio_item))
        
        # 刷新队列显示，高亮当前播放项
        if self.audio_service:
            queue = self.audio_service.get_queue()
            QTimer.singleShot(0, lambda: self._update_playlist_table(queue))
    
    def _on_audio_playback_completed(self, event):
        """
        处理音频播放完成事件
        
        @param event: 事件对象
        """
        # 获取播放完成的音频项
        audio_item = event.data
        
        # 检查是否为临时话术
        is_temporary = audio_item.metadata and audio_item.metadata.get("is_temporary", False)
        
        # if is_temporary:
        #     self.logger.info(f"临时话术播放完成: {audio_item.title}")
        # else:
        #     self.logger.info(f"普通话术播放完成: {audio_item.title}")
        
        # 使用QTimer.singleShot在主线程中发送信号
        QTimer.singleShot(0, lambda: self.playback_completed.emit(audio_item))
        
        # 刷新队列显示，移除高亮
        if self.audio_service:
            queue = self.audio_service.get_queue()
            QTimer.singleShot(0, lambda: self._update_playlist_table(queue))
    
    def _on_audio_playback_stopped(self, event):
        """
        处理音频播放停止事件
        
        @param event: 事件对象
        """
        # 获取停止播放的音频项
        audio_item = event.data
        
        self.logger.info(f"音频播放停止: {audio_item.title}")
        
        # 刷新队列显示，移除高亮
        if self.audio_service:
            queue = self.audio_service.get_queue()
            QTimer.singleShot(0, lambda: self._update_playlist_table(queue))
    
    def _on_audio_playback_paused(self, event):
        """
        处理音频播放暂停事件
        
        @param event: 事件对象
        """
        # 获取暂停播放的音频项
        audio_item = event.data
        
        self.logger.info(f"音频播放暂停: {audio_item.title}")
        
        # 刷新队列显示，移除高亮
        if self.audio_service:
            queue = self.audio_service.get_queue()
            QTimer.singleShot(0, lambda: self._update_playlist_table(queue))
    
    def _on_audio_playback_resumed(self, event):
        """
        处理音频播放恢复事件
        
        @param event: 事件对象
        """
        # 获取恢复播放的音频项
        audio_item = event.data
        
        self.logger.info(f"音频播放恢复: {audio_item.title}")
        
        # 刷新队列显示，高亮当前播放项
        if self.audio_service:
            queue = self.audio_service.get_queue()
            QTimer.singleShot(0, lambda: self._update_playlist_table(queue))
    
    def _on_audio_playback_failed(self, event):
        """
        处理音频播放失败事件
        
        @param event: 事件对象
        """
        # 获取失败播放的音频项
        audio_item = event.data
        
        self.logger.error(f"音频播放失败: {audio_item.title}")
        
        # 刷新队列显示，移除高亮
        if self.audio_service:
            queue = self.audio_service.get_queue()
            QTimer.singleShot(0, lambda: self._update_playlist_table(queue))
    
    def _update_playlist_table(self, queue):
        """
        更新播放队列表格
        
        @param queue: 音频队列
        """
        # 清空表格
        self.playlist_table.setRowCount(0)
        
        # 获取当前播放的音频项
        current_item = None
        if self.audio_service:
            current_item = self.audio_service.get_current_item()
            if current_item:
                self.logger.debug(f"当前播放项: {current_item.title}")
        
        # 计算实际显示项目数（队列长度+当前播放项）
        display_count = len(queue) + (1 if current_item else 0)
        
        # 记录队列中所有文件名，用于调试
        if queue:
            file_names = [item.metadata.get("file_name", "未知文件") if item.metadata else "未知文件" for item in queue]
            self.logger.info(f"更新播放队列表格，队列文件名顺序: {file_names}")
        
        # 添加当前播放项到表格顶部，并高亮显示
        if current_item:
            # 在顶部插入一行
            self.playlist_table.insertRow(0)
            
            # 获取当前字体并设置为加粗
            current_font = self.playlist_table.font()
            bold_font = QFont(current_font)
            bold_font.setBold(True)
            
            # 设置类型
            type_item = QTableWidgetItem("▶ " + self._get_item_type_display(current_item))
            type_item.setForeground(QBrush(QColor("#4CAF50")))  # 绿色文字
            type_item.setFont(bold_font)  # 只设置加粗，不改变大小
            self.playlist_table.setItem(0, 0, type_item)
            
            # 设置内容
            if current_item.source_type == "insert" or current_item.source_type == "ai_reply" or current_item.source_type == "welcome":
                content_item = QTableWidgetItem(current_item.title)
            else:
                file_name = "未知文件"
                if current_item.metadata and "file_name" in current_item.metadata:
                    file_name = current_item.metadata["file_name"]
                content_item = QTableWidgetItem(file_name)
            
            content_item.setForeground(QBrush(QColor("#4CAF50")))  # 绿色文字
            content_item.setFont(bold_font)  # 只设置加粗，不改变大小
            content_item.setToolTip(f"正在播放: {current_item.title}")
            self.playlist_table.setItem(0, 1, content_item)
            
            # self.logger.info(f"已添加当前播放项到表格顶部: {current_item.title}")
        
        # 添加队列项目到表格
        for index, item in enumerate(queue):
            row = self.playlist_table.rowCount()
            self.playlist_table.insertRow(row)
            
            # 设置类型
            type_item = QTableWidgetItem(self._get_item_type_display(item))
            self.playlist_table.setItem(row, 0, type_item)
            
            # 设置内容（对于插播类型，显示实际内容而不是文件名）
            if item.source_type == "insert":
                # 插播类型显示实际内容
                content_item = QTableWidgetItem(item.title)
                self.playlist_table.setItem(row, 1, content_item)
            elif item.source_type == "ai_reply":
                # AI回复类型显示回复内容
                display_content = item.title
                content_item = QTableWidgetItem(display_content)
                self.playlist_table.setItem(row, 1, content_item)
            elif item.source_type == "welcome":
                # 欢迎话术类型显示话术内容
                display_content = item.title
                content_item = QTableWidgetItem(display_content)
                self.playlist_table.setItem(row, 1, content_item)
            else:
                # 其他类型显示文件名
                file_name = "未知文件"
                if item.metadata and "file_name" in item.metadata:
                    file_name = item.metadata["file_name"]
                
                content_item = QTableWidgetItem(file_name)
                self.playlist_table.setItem(row, 1, content_item)
            
            
            # 检查是否为临时话术
            is_temporary = item.metadata and item.metadata.get("is_temporary", False)
            
            # 检查是否正在生成语音
            is_generating = item.metadata and item.metadata.get("generating_audio", False)
            
            # 设置项目背景色
            if is_generating:
                # 正在生成语音的项目使用浅黄色背景
                type_item.setBackground(QBrush(QColor(255, 255, 200)))  # 浅黄色
                content_item.setBackground(QBrush(QColor(255, 255, 200)))
                content_item.setToolTip("正在生成语音...")
                type_item.setToolTip("正在生成语音...")
            elif is_temporary:
                # 临时话术使用浅红色背景
                type_item.setBackground(QBrush(QColor("#FFE0E0")))
                content_item.setBackground(QBrush(QColor("#FFE0E0")))
                # 添加标记
                type_item.setToolTip("临时话术 - 播放完成后将被移除")
                content_item.setToolTip("临时话术 - 播放完成后将被移除")
            else:
                # 为非临时话术添加工具提示，显示实际内容
                content_item.setToolTip(item.title)
        
        # self.logger.info(f"播放队列表格更新完成，共 {self.playlist_table.rowCount()} 行")
    
    def _get_item_type_display(self, item):
        """
        获取项目类型显示文本
        
        @param item: 音频项目
        @return: 显示文本
        """
        # 首先检查元数据中是否有原始类型
        if item.metadata and "original_type" in item.metadata:
            original_type = item.metadata["original_type"]
            # 如果原始类型包含冒号(如"关键词:xxx")，则直接使用原始类型
            if isinstance(original_type, str) and ":" in original_type:
                return original_type
        
        # 类型映射
        type_map = {
            "main_speech": "主话术",
            "insert": "插播",
            "sync": "音画同步",
            "user_enter": "用户提醒", 
            "keyword": "关键词",
            "time": "报时",
            "ai_reply": "AI回复",  # 添加AI回复类型的映射
            "welcome": "欢迎话术"  # 添加欢迎话术类型
        }
        
        # 获取类型显示文本
        type_text = type_map.get(item.source_type, item.source_type)
        
        # 检查是否为临时话术，但对于插播类型、AI回复类型和欢迎话术类型不添加"临时"前缀
        is_temporary = item.metadata and item.metadata.get("is_temporary", False)
        if is_temporary and item.source_type != "insert" and item.source_type != "ai_reply" and item.source_type != "welcome":
            type_text = f"临时{type_text}"
        
        return type_text
    
    def add_to_playlist(self, item_type, content, audio_path=None, is_temporary=False, file_name=None):
        """
        添加到播放队列
        
        @param item_type: 话术类型
        @param content: 话术内容
        @param audio_path: 音频文件路径，如果为None则使用content作为文本生成语音
        @param is_temporary: 是否为临时话术，临时话术会插入到队列顶部且播放完成后移除
        @param file_name: 话术文件名，如果为None则使用"未知文件"
        """
        if not self.audio_service:
            self.logger.warning("无法添加到播放队列：音频服务不可用")
            return
            
        # 如果没有提供文件名，使用默认值
        if not file_name:
            file_name = "未知文件"
            
        # 准备变量字典
        variables = {
            "item_type": item_type,
            "file_name": file_name,
            "is_temporary": is_temporary
        }
        
        # 导入文本处理模块
        from app.utils.speech_text_processor import SpeechTextProcessor

        # 检查是否有随机文本格式或变量需要处理
        if "[" in content or "{" in content:
            # 使用SpeechTextProcessor处理所有变量和随机文本
            processed_content = SpeechTextProcessor.process_speech(content)
            self.logger.info(f"[{item_type}] 已处理变量和随机文本: {processed_content[:50]}...")
        else:
            # 没有特殊格式，直接使用内容
            processed_content = content
            self.logger.info(f"[{item_type}] 无需处理: {processed_content[:50]}...")
        
        # 创建AudioItem对象
        audio_item = AudioItem(
            id=str(uuid.uuid4()),
            file_path=audio_path or "",
            title=processed_content,
            source_type=self._convert_type_to_source_type(item_type),
            metadata={
                "original_type": item_type,
                "is_temporary": is_temporary,
                "file_name": file_name,
                "original_text": content,  # 保存原始文本
                "processed": True  # 标记已处理过文本
            }
        )
        
        # 如果提供了音频路径，直接添加到队列，无需生成语音
        if audio_path:
            self.logger.info(f"使用已提供的音频文件: {audio_path}")
            # 根据是否为临时话术决定添加方式
            if is_temporary:
                # 临时话术插入到队列顶部
                self.audio_service.add_to_queue_front(audio_item)
                self.logger.info(f"已添加临时话术到队列顶部: [{item_type}] {processed_content[:30]}...")
            else:
                # 普通话术添加到队列底部
                self.audio_service.add_to_queue(audio_item)
                self.logger.info(f"已添加话术到队列底部: [{item_type}] {processed_content[:30]}...")
            return
        
        # 如果没有提供音频路径，且语音服务可用，则生成语音
        if not audio_path and self.speech_service:
            # 获取最新配置
            config = AppConfig.get_instance()
            
            # 从ConfigService获取最新配置，确保使用最新设置
            from app.services.service_locator import ServiceLocator
            from app.services.config_service import IConfigService
            config_service = ServiceLocator.get(IConfigService)
            
            # 获取最新的音色设置
            if config_service:
                # 从配置服务获取最新设置
                host_voice = config_service.get("speech.host_voice", "1女10号模型")
                assist_voice = config_service.get("speech.assist_voice", "1女10号模型")  # 保留，暂不使用
                self.logger.info(f"从配置服务获取音色设置: 主播={host_voice}, 辅助={assist_voice}")
            else:
                # 从AppConfig获取设置
                host_voice = config.get("speech", "host_voice", "1女10号模型")
                assist_voice = config.get("speech", "assist_voice", "1女10号模型")  # 保留，暂不使用
            
            self.logger.info(f"当前音色设置：主播音色={host_voice}，辅助音色={assist_voice}，使用主播音色")
            
            # 所有类型都使用主播音色
            speaker = host_voice
            
            self.logger.info(f"使用主播音色生成音频：{speaker}，项目类型：{item_type}，文本: {processed_content[:30]}...")
            
            # 先添加到队列，设置一个标记表示正在生成语音
            audio_item.metadata["generating_audio"] = True
            
            # 根据是否为临时话术决定添加方式
            if is_temporary:
                # 临时话术插入到队列顶部
                self.audio_service.add_to_queue_front(audio_item)
                self.logger.info(f"已添加临时话术到队列顶部: [{item_type}] {processed_content[:30]}...")
            else:
                # 普通话术添加到队列底部
                self.audio_service.add_to_queue(audio_item)
                self.logger.info(f"已添加话术到队列底部: [{item_type}] {processed_content[:30]}...")
            
            # 定义完成回调函数
            def on_speech_completed(text, file_path):
                if file_path:
                    self.logger.info(f"语音生成完成: {text[:30]}..., 文件路径: {file_path}")
                    
                    # 查找队列中的音频项并更新文件路径
                    updated = False
                    if self.audio_service:
                        queue = self.audio_service.get_queue()
                        for item in queue:
                            if item.id == audio_item.id:
                                item.file_path = file_path
                                item.metadata.pop("generating_audio", None)  # 移除生成中标记
                                updated = True
                                self.logger.info(f"已更新音频项 {item.id} 的文件路径: {file_path}")
                                break
                        
                        # 如果找到并更新了项目，触发队列更新事件
                        if updated:
                            self.audio_service.notify("audio_queue_updated", queue)
                        else:
                            self.logger.warning(f"未找到音频项 {audio_item.id} 用于更新文件路径")
                else:
                    self.logger.error(f"语音生成失败: {text[:30]}...")
            
            # 调用异步方法生成语音
            self.speech_service.synthesize_async(
                text=processed_content,
                speaker=speaker,
                completion_callback=on_speech_completed
            )
            
            return  # 直接返回，不执行后面的添加逻辑
    
    def _convert_type_to_source_type(self, item_type):
        """
        将项目类型转换为源类型
        
        @param item_type: 项目类型
        @return: 源类型
        """
        # 处理包含冒号的类型字符串，例如"关键词:xxx"
        if isinstance(item_type, str) and ":" in item_type:
            # 提取冒号前的部分作为基本类型
            base_type = item_type.split(":", 1)[0].strip()
            self.logger.debug(f"从类型 '{item_type}' 中提取基本类型: '{base_type}'")
            item_type = base_type

        type_map = {
            "主话术": "main_speech",
            "插播": "insert",
            "关键词": "keyword",
            "音画同步": "sync",
            "用户提醒": "user_enter",
            "AI回复": "ai_reply",  # 添加AI回复类型
            "欢迎话术": "welcome"  # 添加欢迎话术类型
        }
        return type_map.get(item_type, "unknown")
    
    def clear_playlist(self):
        """
        清空播放队列
        """
        if self.audio_service:
            self.audio_service.clear_queue()
        else:
            self.playlist_table.setRowCount(0)
            
        self.logger.info("已清空播放队列")
    
    def get_queue_items(self):
        """
        获取队列项目
        
        @return: 队列项目列表
        """
        if self.audio_service:
            return self.audio_service.get_queue()
        return []
        
    def add_temporary_speech(self, item_type, content, audio_path=None, file_name=None):
        """
        添加临时话术到队列顶部
        
        @param item_type: 话术类型
        @param content: 话术内容
        @param audio_path: 音频文件路径，如果为None则使用content作为文本生成语音
        @param file_name: 话术文件名，如果为None则使用"未知文件"
        """
        self.add_to_playlist(item_type, content, audio_path, is_temporary=True, file_name=file_name)
    
    def _init_queue(self):
        """
        初始化队列，添加所有主话术文件到队列
        """
        try:
            self.logger.info("正在初始化队列...")
            
            # 检查队列是否为空
            if self.audio_service and not self.audio_service.get_queue():
                self.logger.info("队列为空，添加所有主话术文件")
                
                # 检查主话术管理器是否可用
                if hasattr(self.audio_service, 'main_speech_manager') and self.audio_service.main_speech_manager:
                    # 获取所有主话术文件
                    main_speech_files = self.audio_service.main_speech_manager.get_all_speech_files()
                    
                    if main_speech_files:
                        self.logger.info(f"找到 {len(main_speech_files)} 个主话术文件")
                        
                        # 按照文件名中的数字进行排序
                        def extract_number(filename):
                            # 提取文件名中的第一个数字部分
                            import re
                            # 特别处理文件名开头的数字
                            match = re.match(r'^(\d+)', filename)
                            if match:
                                num = int(match.group(1))
                                return num
                            return float('inf')  # 如果没有数字，将其排在最后
                        
                        # 按照数字顺序排序文件名，并打印排序前后的顺序
                        sorted_files = sorted(main_speech_files.keys(), key=extract_number)
                        
                        # 获取主播音色设置
                        config = AppConfig.get_instance()
                        from app.services.service_locator import ServiceLocator
                        from app.services.config_service import IConfigService
                        config_service = ServiceLocator.get(IConfigService)
                        
                        # 获取最新的音色设置
                        if config_service:
                            host_voice = config_service.get("speech.host_voice", "1女10号模型")
                            self.logger.info(f"从配置服务获取主播音色: {host_voice}")
                        else:
                            host_voice = config.get("speech", "host_voice", "1女10号模型")
                            self.logger.info(f"从AppConfig获取主播音色: {host_voice}")
                        
                        # 遍历排序后的主话术文件
                        for file_name in sorted_files:
                            content = main_speech_files[file_name]
                            # 如果内容不为空，随机选择一行
                            if content:
                                # 将内容按行分割
                                lines = [line.strip() for line in content.split('\n') if line.strip()]
                                
                                # 如果有有效行，随机选择一行
                                if lines:
                                    import random
                                    speech_content = random.choice(lines)
                                    
                                    self.logger.info(f"从文件 {file_name} 中随机选择一行: {speech_content[:20]}...")
                                    
                                    # 准备变量字典
                                    variables = {
                                        "item_type": "主话术",
                                        "file_name": file_name,
                                        "is_temporary": False
                                    }
                                    
                                    # 检查是否有随机文本格式需要处理
                                    has_random_text = "{" in speech_content and "|" in speech_content and "}" in speech_content
                                    if has_random_text:
                                        self.logger.info(f"[文本处理] 检测到随机文本格式: {speech_content[:50]}...")
                                    
                                    # 使用SpeechTextProcessor完整处理话术内容
                                    processed_content = SpeechTextProcessor.process_speech(speech_content, variables)
                                    
                                    self.logger.info(f"[文本处理] 原始文本: {speech_content[:50]}...")
                                    self.logger.info(f"[文本处理] 处理后文本: {processed_content[:50]}...")
                                    
                                    # 创建音频项，使用主播音色设置
                                    audio_item = AudioItem(
                                        id=str(uuid.uuid4()),
                                        file_path="",
                                        title=processed_content,
                                        source_type="main_speech",
                                        metadata={
                                            "original_type": "主话术", 
                                            "is_temporary": False,
                                            "file_name": file_name,
                                            "original_text": speech_content,  # 保存原始文本
                                            "processed": True,  # 标记已处理过文本
                                            "host_voice": host_voice  # 记录使用的主播音色
                                        }
                                    )
                                    
                                    # 添加到队列底部
                                    self.audio_service.add_to_queue(audio_item)
                                    
                                    self.logger.info(f"已添加主话术到队列，使用主播音色 {host_voice}: 文件 {file_name}")
                                else:
                                    self.logger.warning(f"文件 {file_name} 没有有效行")
                            else:
                                self.logger.warning(f"文件 {file_name} 内容为空")
                    else:
                        self.logger.warning("没有找到主话术文件")
                else:
                    # 尝试从配置服务获取配置路径
                    from app.services.service_locator import ServiceLocator
                    from app.services.config_service import IConfigService
                    
                    config_service = ServiceLocator.get(IConfigService)
                    if config_service:
                        # 从用户配置中获取路径
                        config_path = config_service.get("app.config_path")
                        if config_path:
                            self.logger.info(f"尝试使用用户配置的路径初始化主话术管理器: {config_path}")
                            try:
                                from app.services.main_speech_manager import MainSpeechManager
                                self.audio_service.main_speech_manager = MainSpeechManager(config_path)
                                self.logger.info("主话术管理器初始化成功，重新尝试加载主话术")
                                # 递归调用自身，重新尝试加载主话术
                                self._init_queue()
                                return
                            except Exception as e:
                                self.logger.error(f"初始化主话术管理器失败: {str(e)}")
                    
                    self.logger.warning("主话术管理器不可用")
            else:
                self.logger.info("队列不为空或音频服务不可用，跳过初始化")
            
            # 手动刷新播放队列表格
            self.refresh_playlist()
        except Exception as e:
            self.logger.error(f"初始化队列失败: {str(e)}")
    
    def refresh_playlist(self):
        """
        手动刷新播放队列表格
        """
        try:
            if self.audio_service:
                queue = self.audio_service.get_queue()
                self.logger.info(f"手动刷新播放队列表格，队列长度: {len(queue)}")
                self._update_playlist_table(queue)
            else:
                self.logger.warning("无法刷新播放队列表格：音频服务不可用")
        except Exception as e:
            self.logger.error(f"刷新播放队列表格失败: {str(e)}")
    
    def reload_all_speeches(self):
        """
        重新加载所有主话术文件到队列中
        根据当前播放状态决定是否生成音频：
        - 如果正在播放（显示停止按钮），则生成音频
        - 如果未播放（显示开始按钮），则只加载列表不生成音频
        """
        try:
            self.logger.info("正在重新加载所有主话术文件...")
            
            # 检查音频服务是否可用
            if not self.audio_service:
                self.logger.warning("音频服务不可用，无法重新加载主话术")
                return
            
            # 判断当前播放状态
            is_playing = False
            if self.audio_service.is_playing():
                is_playing = True
                self.logger.info("当前正在播放中，刷新后将生成音频")
            else:
                self.logger.info("当前未播放，刷新后将只加载列表不生成音频")
            
            # 获取最新的配置路径和话术来源
            config_service = ServiceLocator.get(IConfigService)
            if config_service:
                config_path = config_service.get("app.config_path")
                speech_source = config_service.get("app.speech_source", "主话术")
                
                if config_path:
                    self.logger.info(f"使用最新配置路径: {config_path}")
                    self.logger.info(f"当前话术来源: {speech_source}")
                    
                    # 更新主话术管理器的配置路径和话术来源
                    if not self.audio_service.main_speech_manager:
                        from app.services.main_speech_manager import MainSpeechManager
                        self.audio_service.main_speech_manager = MainSpeechManager(config_path)
                        self.audio_service.main_speech_manager.set_speech_source(speech_source)
                        self.logger.info(f"已创建主话术管理器，配置路径: {config_path}")
                    else:
                        if self.audio_service.main_speech_manager.config_path != config_path:
                            self.audio_service.main_speech_manager = MainSpeechManager(config_path)
                            self.logger.info(f"已更新主话术管理器配置路径: {config_path}")
                        self.audio_service.main_speech_manager.set_speech_source(speech_source)
                        self.logger.info(f"已更新话术来源: {speech_source}")
            
            # 清空当前队列
            self.audio_service._queue.clear()
            self.logger.info("已清空播放队列")
            
            # 重新加载主话术管理器
            if hasattr(self.audio_service, 'main_speech_manager') and self.audio_service.main_speech_manager:
                self.audio_service.main_speech_manager.reload()
                
                # 获取所有主话术文件
                main_speech_files = self.audio_service.main_speech_manager.get_all_speech_files()
                
                if main_speech_files:
                    # 过滤只保留 .txt 文件
                    txt_files = {k: v for k, v in main_speech_files.items() if k.lower().endswith('.txt')}
                    self.logger.info(f"找到 {len(txt_files)} 个txt话术文件")
                    
                    # 如果没有找到txt文件，确保更新UI显示空列表
                    if not txt_files:
                        self.logger.warning("没有找到txt话术文件")
                        # 刷新播放列表显示空列表
                        self.refresh_playlist()
                        return
                    
                    # 遍历所有主话术文件
                    for file_name, content in txt_files.items():
                        # 如果内容不为空，随机选择一行
                        if content:
                            # 将内容按行分割
                            lines = [line.strip() for line in content.split('\n') if line.strip()]
                            
                            if lines:
                                # 随机选择一行
                                speech_content = random.choice(lines)
                                
                                # 创建音频项
                                from app.models.audio_item import AudioItem
                                import uuid
                                
                                audio_item = AudioItem(
                                    id=str(uuid.uuid4()),
                                    file_path="",  # 文件路径会在生成音频时设置
                                    title=speech_content,
                                    source_type="main_speech",
                                    metadata={
                                        "original_type": "主话术",
                                        "is_temporary": False,
                                        "file_name": file_name,
                                        "original_text": speech_content
                                    }
                                )
                                
                                # 添加到队列
                                self.audio_service.add_to_queue(audio_item)
                                self.logger.debug(f"已添加主话术到队列: {speech_content[:30]}...")
                
                    # 刷新播放列表显示
                    self.refresh_playlist()
                    self.logger.info("已完成主话术重新加载")
                    
                    # 只在播放状态下生成音频
                    if is_playing:
                        # 确保队列前两项有音频文件
                        self.audio_service._ensure_front_items_have_audio()
                        self.logger.info("已为队列前项生成音频文件")
                    else:
                        self.logger.info("当前为停止状态，不生成音频文件")
                else:
                    self.logger.warning("没有找到主话术文件")
                    # 刷新播放列表显示空列表
                    self.refresh_playlist()
            
        except Exception as e:
            self.logger.error(f"重新加载主话术失败: {str(e)}")
            import traceback
            self.logger.error(traceback.format_exc())
            # 出现异常时也确保刷新UI显示
            self.refresh_playlist()
    
    def _stop_playback(self):
        """
        停止当前播放
        """
        try:
            if self.audio_service:
                success = self.audio_service.stop_playback()
                if success:
                    self.logger.info("已停止当前播放")
                else:
                    self.logger.warning("停止播放失败")
            else:
                self.logger.warning("无法停止播放：音频服务不可用")
        except Exception as e:
            self.logger.error(f"停止播放失败: {str(e)}")

    def _refresh_current_playback(self):
        """
        刷新当前播放状态
        """
        try:
            if self.audio_service:
                # 获取完整队列
                queue = self.audio_service.get_queue()
                
                # 获取当前播放项
                current_item = self.audio_service.get_current_item()
                
                # 只有当有当前播放项或队列不为空时才刷新
                if current_item or queue:
                    self._update_playlist_table(queue)
            else:
                self.logger.debug("无法刷新当前播放状态：音频服务不可用")
        except Exception as e:
            self.logger.error(f"刷新当前播放状态失败: {str(e)}")

    def generate_and_play_audio(self):
        """
        为队列中的前三个项目生成音频并开始播放
        
        Returns:
            bool: 是否成功开始生成和播放
        """
        self.logger.info("开始生成和播放音频")
        
        # 重置停止标志
        self._generation_stopped = False
        
        # 检查服务是否可用
        if not self.audio_service:
            self.logger.error("无法生成和播放音频：音频服务不可用")
            return False
            
        if not self.speech_service:
            self.logger.error("无法生成和播放音频：语音服务不可用")
            return False
            
        # 获取队列
        queue = self.audio_service.get_queue()
        if not queue:
            self.logger.warning("无法生成和播放音频：队列为空")
            return False
            
        # 设置播放状态为PLAYING
        if self.state_manager:
            self.logger.info("设置播放状态为PLAYING")
            self.state_manager.set_state(StateType.PLAYBACK, StateValues.PLAYING)
            
        # 检查前三个项目是否已有音频文件
        items_to_generate = []
        for i, item in enumerate(queue[:3]):
            if not item.file_path or not os.path.exists(item.file_path):
                items_to_generate.append(item)
                
        if not items_to_generate:
            # 如果所有音频文件都已存在，直接开始播放
            self.logger.info("所有音频文件都已存在，直接开始播放")
            success = self.audio_service.start_playback()
            return success
            
        # 记录需要生成的总数和已完成数
        self._generation_total = len(items_to_generate)
        self._generation_completed = 0
        
        # 创建锁，用于同步计数
        if not hasattr(self, '_generation_lock'):
            self._generation_lock = threading.Lock()
        
        # 启动生成线程
        self.logger.info(f"需要生成 {len(items_to_generate)} 个音频文件")
        generation_thread = threading.Thread(
            target=self._generate_audio_for_items,
            args=(items_to_generate,)
        )
        generation_thread.daemon = True
        generation_thread.start()
        
        return True

    def stop_generation(self):
        """
        停止音频生成
        """
        self.logger.info("停止音频生成")
        self._generation_stopped = True

    def _generate_audio_for_items(self, items_to_generate):
        """
        生成音频文件的线程函数
        
        Args:
            items_to_generate: 需要生成音频的项目列表
        """
        try:
            # 获取最新配置
            config = AppConfig.get_instance()
            
            # 获取语音服务和音频服务
            speech_service = self.speech_service
            audio_service = self.audio_service
            
            if not speech_service or not audio_service:
                self.logger.error("无法生成音频：服务不可用")
                return
                
            # 获取最新的音色设置
            from app.services.service_locator import ServiceLocator
            from app.services.config_service import IConfigService
            config_service = ServiceLocator.get(IConfigService)
            
            # 获取最新的音色设置
            if config_service:
                host_voice = config_service.get("speech.host_voice", "1女10号模型")
                assist_voice = config_service.get("speech.assist_voice", "31男13号模型")
            else:
                host_voice = config.get("speech", "host_voice", "1女10号模型")
                assist_voice = config.get("speech", "assist_voice", "31男13号模型")
                
            self.logger.info(f"当前音色设置：主播音色={host_voice}，辅助音色={assist_voice}，使用主播音色")
            
            # 获取队列
            queue = audio_service.get_queue()
            
            # 遍历需要生成的项目
            for item in items_to_generate:
                # 如果已停止，则退出生成
                if self._generation_stopped:
                    self.logger.info("检测到停止标志，中止音频生成")
                    return
                    
                # 跳过已有音频文件的项目
                if item.file_path and os.path.exists(item.file_path):
                    continue
                    
                # 获取该项的内容和类型
                text = item.title
                item_type = item.source_type
                
                # 标记为正在生成音频
                item.metadata["generating_audio"] = True
                
                # 触发队列更新事件
                audio_service.notify("audio_queue_updated", queue)
                
                # 创建回调函数
                def create_callback(current_item):
                    def on_speech_completed(text, file_path):
                        with self._generation_lock:
                            if file_path:
                                self.logger.info(f"语音生成完成: {text[:30]}..., 文件路径: {file_path}")
                                # 更新队列项的音频路径
                                current_item.file_path = file_path
                                current_item.metadata.pop("generating_audio", None)
                                
                                # 更新计数
                                self._generation_completed += 1
                                
                                # 触发队列更新事件
                                audio_service.notify("audio_queue_updated", queue)
                            else:
                                self.logger.error(f"语音生成失败: {text[:30]}...")
                                # 更新计数，即使失败也继续处理下一个
                                self._generation_completed += 1
                            
                            # 如果全部生成完成且未停止，开始播放
                            if self._generation_completed >= self._generation_total and not self._generation_stopped:
                                self.logger.info("所有音频生成完成，开始播放")
                                # 使用QTimer.singleShot确保在主线程中调用
                                from PyQt5.QtCore import QTimer
                                QTimer.singleShot(0, self._start_playback_after_generation)
                    
                    return on_speech_completed
                
                # 根据类型选择音色
                voice = host_voice
                # 如果是AI回复或者关键词等特殊类型，可能使用辅助音色
                if item_type in ["ai_reply", "keyword"]:
                    voice = assist_voice
                
                # 生成语音
                self.logger.info(f"开始生成语音: {text[:30]}... 类型: {item_type}, 音色: {voice}")
                speech_service.synthesize_async(
                    text=text,
                    speaker=voice,
                    completion_callback=create_callback(item)
                )
            
        except Exception as e:
            self.logger.error(f"生成语音失败: {str(e)}")
            traceback.print_exc()

    def _start_playback_after_generation(self):
        """
        在音频生成完成后开始播放
        """
        try:
            # 如果已停止，则不开始播放
            if self._generation_stopped:
                self.logger.info("检测到停止标志，不开始播放")
                return
                
            if not self.audio_service:
                self.logger.error("无法开始播放：音频服务不可用")
                return
                
            self.logger.info("尝试在音频生成后开始播放")
            
            # 设置播放状态为PLAYING
            if self.state_manager:
                self.logger.info("设置播放状态为PLAYING")
                self.state_manager.set_state(StateType.PLAYBACK, StateValues.PLAYING)
            
            # 直接调用音频服务的播放方法
            success = self.audio_service.start_playback()
            
            if success:
                self.logger.info("成功开始播放音频")
            else:
                self.logger.error("开始播放音频失败")
                # 如果播放失败，恢复状态为READY
                if self.state_manager:
                    self.logger.info("播放失败，恢复播放状态为READY")
                    self.state_manager.set_state(StateType.PLAYBACK, StateValues.READY)
                    
        except Exception as e:
            self.logger.error(f"在音频生成后开始播放时发生错误: {str(e)}")
            traceback.print_exc()

    def _on_playback_completed(self, audio_item):
        """
        处理播放完成事件
        
        Args:
            audio_item: 完成播放的音频项
        """
        self.logger.info(f"播放完成: {audio_item.title if audio_item else 'Unknown'}")
        
        # 更新播放状态为READY
        if self.state_manager:
            self.logger.info("更新播放状态为READY")
            self.state_manager.set_state(StateType.PLAYBACK, StateValues.READY)
        
        # 刷新UI
        self._refresh_ui()
        
        # 发出播放完成信号
        self.playback_stopped_signal.emit()
        
        # 如果队列为空，通知界面更新
        queue = self.audio_service.get_queue() if self.audio_service else []
        if not queue:
            self.queue_updated_signal.emit()

    def _refresh_ui(self):
        """
        刷新UI
        """
        # 实现刷新UI的逻辑
        pass

    def _on_playback_started(self, audio_item):
        """
        处理播放开始事件
        
        Args:
            audio_item: 开始播放的音频项
        """
        self.logger.info(f"播放开始: {audio_item.title if audio_item else 'Unknown'}")
        
        # 刷新UI
        self._refresh_ui()
        
        # 发出播放开始信号
        self.playback_started_signal.emit()

    def _on_playback_error(self, error):
        """
        处理播放错误事件
        
        Args:
            error: 播放错误
        """
        self.logger.error(f"播放错误: {error}")
        
        # 刷新UI
        self._refresh_ui()

    def _on_queue_updated(self, queue):
        """
        处理队列更新事件
        
        Args:
            queue: 更新后的音频队列
        """
        # self.logger.info(f"队列更新: {len(queue)} 项")
        
        # 刷新UI
        self._refresh_ui()
        
        # 发出生成进度信号
        self.queue_updated.emit(queue)

    def _on_generation_progress(self, current, total):
        """
        处理生成进度事件
        
        Args:
            current: 当前生成进度
            total: 总进度
        """
        self.logger.info(f"生成进度: {current}/{total}")
        
        # 刷新UI
        self._refresh_ui()
        
        # 发出生成进度信号
        self.generation_progress_signal.emit(current, total)

    def _refresh_playback_status(self):
        """
        刷新播放状态
        """
        try:
            if self.audio_service:
                # 获取完整队列
                queue = self.audio_service.get_queue()
                
                # 获取当前播放项
                current_item = self.audio_service.get_current_item()
                
                # 只有当有当前播放项或队列不为空时才刷新
                if current_item or queue:
                    self._update_playlist_table(queue)
            else:
                self.logger.debug("无法刷新播放状态：音频服务不可用")
        except Exception as e:
            self.logger.error(f"刷新播放状态失败: {str(e)}") 